package phototagger.imgsim;

import java.awt.image.BufferedImage;
import javax.imageio.*;

/** A YIQImage is a representation of a BufferedImage object which has
    been converted to the YIQ color space. Internally, this representation
    is stored as 3 two dimensional arrays (one each for the Y, I, and Q
    channels). The constructor for YIQ image handles the rescaling and
    conversion process. **/
public class YIQImage {
	
    public static final int MIN_IMAGE_WIDTH     = 128;
    public static final int MIN_IMAGE_HEIGHT    = 128;
    public static final int SCALED_IMAGE_WIDTH  = 128;
    public static final int SCALED_IMAGE_HEIGHT = 128;

	public static final int NUM_LARGEST   =  60;
    public static final boolean DEBUG     = true;

    private static final double NO_VALUE  = -666.666;

    public static final String COLORSPACE = "YIQ";

    private double[][] Yelements;
    private double[][] Ielements;
    private double[][] Qelements;

    private double Yavg = NO_VALUE;
    private double Iavg = NO_VALUE;
    private double Qavg = NO_VALUE;

    private int height;
    private int width;
    
    private BufferedImage original;

    /** Constructor when you don't have an image to make a YIQImage out of,
	you just want to fill out its arrays, baby.
    **/
    public YIQImage() {}

    public YIQImage(BufferedImage image) throws Exception {
	width  = image.getWidth();
	height = image.getHeight();
	// rescale the image

	Converter convert = new Converter(image, YIQImage.COLORSPACE);
	Yelements = convert.getElements('Y');
	Ielements = convert.getElements('I');
	Qelements = convert.getElements('Q');
    }

    /** Constructor for testing purposes. You don't need to specify a
	BufferedImage, just give it the Y,I, and Q arrays.
	TODO: this should perform error checking for size.
    **/
    public YIQImage(double[][] Ychannel, double[][] Ichannel, 
		    double[][] Qchannel) {
	Yelements = Ychannel;
	Ielements = Ichannel;
	Qelements = Qchannel;
    }

    /** TODO: this should do checking on element size, yeah...**/
    public void setElements(char color, double[][] elements) {
	switch(color) {
	case 'Y':
	    Yelements = elements;
	    break;
	case 'I':
	    Ielements = elements;
	    break;
	case 'Q':
	    Qelements = elements;
	    break;
	}
    }

    public String getColorSpace() {
	return COLORSPACE;
    }

    public double getAverage(char color) {
	double avg;
	switch(color) {
	case 'Y':
	    avg = Yavg;
	    break;
	case 'I':
	    avg = Iavg;
	    break;
	case 'Q':
	    avg = Qavg;
	    break;
	default:
	    avg = NO_VALUE;
	}
	if(avg == NO_VALUE) {
	    String m = "Oops, you tried to get the average color " +
		"for an image that hasn't had it set yet. " + 
		"That probably means this image isn't being used to hold " +
		"decomposed data.";
	    throw new IllegalStateException(m);
	}
	return avg;
    }

    public void setAverage(char color, double avg) {
	switch(color) {
	case 'Y':
	    Yavg = avg;
	    break;
	case 'I':
	    Iavg = avg;
	    break;
	case 'Q':
	    Qavg = avg;
	    break;
	}
    }

    public double[][] getElements(char color) {
	double[][] elements;
	switch (color) {
	case 'Y':
	    elements = Yelements;
	    break;
	case 'I':
	    elements = Ielements;
	    break;
	case 'Q':
	    elements = Qelements;
	    break;
	default:
	    return null;
	}

	return elements;
    }

    public int getHeight() {
	return height;
    }

    public int getWidth() {
	return width;
    }


    /** TODO: This needs to be configurable for wavelet type!
	Probably get from a a properties file. Eventually.
	@returns the Signature for this Image **/
    public YIQSignature getSignature() {
	HaarWavelet wavelet = new HaarWavelet();
	YIQImage decomposed = wavelet.decompose(this);
		
	YIQImage truncated = truncate(decomposed, NUM_LARGEST);
	YIQSignature quantized = quantize(truncated);
	return quantized;
    }
    
    private YIQSignature quantize(YIQImage image) {
    	String colorspace = image.getColorSpace();
    	char firstColor = colorspace.charAt(0);
    	char secondColor = colorspace.charAt(1);
    	char thirdColor = colorspace.charAt(2);
    	double[][] firstTrunc  = image.getElements(firstColor);
    	double[][] secondTrunc = image.getElements(secondColor);
    	double[][] thirdTrunc  = image.getElements(thirdColor);
    	int[][] firstQuant = new int
    	    [firstTrunc.length]			
    	    [(firstTrunc[0]).length];
    	
    	int[][] secondQuant = new int
    	    [secondTrunc.length]
    	    [(secondTrunc[0]).length];
    	
    	int[][] thirdQuant = new int
    	    [thirdTrunc.length]
    	    [(thirdTrunc[0]).length];

    	// quantize the coeffients
    	for(int i=0; i < firstTrunc.length; i++) {
    	    for(int j=0; j < (firstTrunc[0]).length; j++) {
    	    	{ double val = firstTrunc[i][j]; if(val > 0d) { val = 1; } else if(val < 0d) { val = -1; } else { val = 0; }; firstQuant[i][j] = (int)val; }
    	    	{ double val = secondTrunc[i][j]; if(val > 0d) { val = 1; } else if(val < 0d) { val = -1; } else { val = 0; }; secondQuant[i][j] = (int)val; }	
    	    	{ double val = thirdTrunc[i][j]; if(val > 0d) { val = 1; } else if(val < 0d) { val = -1; } else { val = 0; }; thirdQuant[i][j] = (int)val; }	
    	    }
    	}
    	YIQSignature sig = new YIQSignature(firstQuant, secondQuant, thirdQuant,
    					 image.getAverage('Y'),
    					 image.getAverage('I'),
    					 image.getAverage('Q'));
    	
    	return sig;
        }

	private YIQImage truncate(YIQImage image, int largest) {
		String colorspace = image.getColorSpace();
		char firstColor  = colorspace.charAt(0);
		char secondColor = colorspace.charAt(1);
		char thirdColor  = colorspace.charAt(2);

			
		double[][] firstElem  = image.getElements(firstColor);
		double[][] secondElem = image.getElements(secondColor);
		double[][] thirdElem  = image.getElements(thirdColor);

		// set average color position (which is located at (0,0) to 0 so 
		// it doesn't mess up the search for the largest wavelet coeffients.
		
		firstElem[0][0]  = 0d;
		secondElem[0][0] = 0d;
		thirdElem[0][0]  = 0d;
		
		CoeffientList firstCoeffientList  = new CoeffientList(firstElem);
		CoeffientList secondCoeffientList = new CoeffientList(secondElem);
		CoeffientList thirdCoeffientList  = new CoeffientList(thirdElem);
		
		firstElem  = firstCoeffientList.getLargest(largest);
		secondElem = secondCoeffientList.getLargest(largest);
		thirdElem  = thirdCoeffientList.getLargest(largest);
		
		image.setElements(firstColor, firstElem);
		image.setElements(secondColor, secondElem);
		image.setElements(thirdColor, thirdElem);
		
		return image;
	    }

    
}
